package com.springboot.demo.controller;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import org.I0Itec.zkclient.ZkClient;
import org.apache.curator.framework.CuratorFramework;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.springboot.common.lock.LockFactory;
import com.springboot.common.util.SafeNullUtils;
import com.springboot.db.entity.User;
import com.springboot.demo.config.CustomConfig;
import com.springboot.demo.config.DemoConfig;
import com.springboot.demo.config.MyConfig;
import com.springboot.demo.config.zookeeper.ZookeeperConfigService;
import com.springboot.demo.dao.UserDao;
import com.springboot.demo.lock.user.MultiLockFactory;
import com.springboot.demo.lock.user.UserLock;
import com.springboot.demo.lock.zookeeper.ZkLock;
import com.springboot.demo.util.redis.RedisUtil;
import com.springboot.mongo.dao.UserLoginDAO;
import com.springboot.mongo.entity.UserLoginRecord;

import lombok.extern.log4j.Log4j2;
import static org.springframework.data.mongodb.core.query.Criteria.where;

/**
 * 测试controller
 *
 * @author supu
 * @create 2018-12-12 13:34
 **/
@Log4j2
@RestController
public class TestController {
    
    /**
     * {@link UserDao#selectUsers()}
     */
    @Autowired
    private UserDao userDao;
    @Autowired
    private Environment environment;
    @Autowired
    private MyConfig myConfig;
    @Autowired
    private CustomConfig customConfig;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ZookeeperConfigService zookeeperConfigService;
    @Autowired
    private ZkClient zkClient;
    @Autowired
    private MultiLockFactory multiLockFactory;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private CuratorFramework curatorFramework;
    @Autowired
    private DemoConfig demoConfig;
    @Autowired
    private UserLoginDAO userLoginDAO;
    @Autowired
    private MongoTemplate mongoTemplate;

    @GetMapping("/index")
    public void index(){
        /*String[] names = processedProfiles.stream()
                .filter((profile) -> profile != null && !profile.isDefaultProfile())
                .map(ConfigFileApplicationListener.Profile::getName).toArray(String[]::new);*/
        log.info(JSONObject.toJSONString(userDao.selectUsers()));


        log.error(demoConfig.getString(DemoConfig.ZOOKEEPER_ADDRESS));
    }

    @GetMapping("/test")
    @ResponseBody
    public String testEnv(){
        log.info(environment.getProperty("env"));
        log.info(environment.getProperty("server.port"));
        log.info("webPath:" + environment.getProperty("web.path"));
        log.info(environment.getProperty("custom.name"));
        log.info(environment.getProperty("custom.age"));
        log.info("my.domain:" + environment.getProperty("my.domain"));
        log.warn("customHost:" + environment.getProperty("custom.host"));
        log.error("test.host:" + environment.getProperty("test.host"));
        log.debug("self.host:" + environment.getProperty("self.host"));

        log.info("myConfig.test.host:" + myConfig.getHost());
        log.info("customConfig.customHost:" + customConfig.getCustomHost());
        log.info("customConfig.selfHost:" + customConfig.getSelfHost());
        return JSONObject.toJSONString(userDao.selectUsers());
    }

    @GetMapping("/thread")
    @ResponseBody
    public String thread(){
        new Thread(() ->{

            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("thread run test");
        }).start();

        return "thread run test";
    }

    @GetMapping("/redis")
    @ResponseBody
    public String redisTest(){


        List<User> users = userDao.selectUsers();

        redisUtil.setValue("users", users, 5 * 60);

        List<User> cacheUsers = (List<User>)redisUtil.getValue("users");
        log.error(JSONObject.toJSONString(cacheUsers));

        Map<String, Object> map = new HashMap<>(2);
        users.forEach(user -> {
            map.put(user.getName(), user);
        });
        redisUtil.setValue("map", map, 5 * 60);

        Map<String, Object> cacheMap = (Map<String, Object>)redisUtil.getValue("map");
        log.warn(JSONObject.toJSONString(cacheMap));
        log.warn(cacheMap.get("zs"));

        return "redis test";
    }

    @GetMapping("/zk")
    @ResponseBody
    public String zkTest(String node){
        /*Watcher watcher= new Watcher(){
            @Override
            public void process(WatchedEvent event) {
                log.warn("receive event：{}", event);
            }
        };

        String value = null;
        try {
            final ZooKeeper zookeeper = new ZooKeeper("192.168.2.241:2181", 10000, watcher);
            final byte[] data = zookeeper.getData("/springBootDemo/demo", watcher, null);
            value = new String(data);
            zookeeper.close();
        }catch(Exception e){
            e.printStackTrace();
        }*/


       /* String rootPath = environment.getProperty("zookeeper.root.node");

       *//* List<User> users = userDao.selectUsers();

        zkClient.create(rootPath+"/users", JSONObject.toJSONString(users), CreateMode.PERSISTENT);*//*

        log.error(zookeeperConfigService.getValue("test.demo"));
        log.error(zookeeperConfigService.getValue("test"));
        log.error(zookeeperConfigService.getValue("demo"));

        List<User> zkUsers = JSONObject.parseArray(zookeeperConfigService.getValue("users"), User.class);
        zkUsers.forEach(user -> {
            log.error(user.getId() + user.getName() + user.getSex());
        });


        List<String> list = zkClient.getChildren(rootPath);
        list.forEach(s -> {
            log.warn("key: {} 对应的 value:{}", s,zkClient.readData(rootPath + "/" + s, true));
        });*/

      /* String watchNodePath = environment.getProperty("zookeeper.watch.node");

       if(!zkClient.exists(watchNodePath)){
           zkClient.createPersistent(watchNodePath, true);
       }

       zkClient.createEphemeral(watchNodePath+"/"+node, node);*/

        ZkLock zkLock = new ZkLock(zkClient,"/lock/springBootDemo/user","lock_");

        for(int i = 0; i < 10; i++){
            new Thread(() -> {
                try {
                    zkLock.tryLock(1000,TimeUnit.MILLISECONDS);
                    log.info("{} 正在进行处理", Thread.currentThread().getName());
                } catch (Exception e) {
                    log.error("{} 加锁异常：{}", Thread.currentThread().getName(), e);
                } finally {
                    try {
                        zkLock.unLock();
                    } catch (Exception e) {
                        log.error("{} 释放锁异常：{}", Thread.currentThread().getName(), e);
                    }
                }
            }).start();
        }

        return "zk test";
    }

    @GetMapping("/curator")
    @ResponseBody
    public String curatorTest(){
        UserLock userLock = multiLockFactory.getUserLock(Arrays.asList(1,2,3,4,5));
        userLock.lock();

        userLock.unLock();

       /* Lock lock = lockFactory.getLock("user");

        for(int i = 0; i < 10; i++){
            new Thread(() -> {
                try {
                    boolean isSuccess = lock.tryLock(1000,TimeUnit.MILLISECONDS);
                    log.info("{} 正在进行处理", Thread.currentThread().getName());
                    log.info("{} 获取锁 {}", Thread.currentThread().getName(), (isSuccess ? "sucess" : "fail"));
                } catch (Exception e) {
                    log.error("{} 加锁异常：{}", Thread.currentThread().getName(), e);
                } finally {
                    try {
                        lock.unLock();
                    } catch (Exception e) {
                        log.error("{} 释放锁异常：{}", Thread.currentThread().getName(), e);
                    }
                }
            }).start();
        }*/
        return "zk test";
    }

    @GetMapping("/curator/test")
    @ResponseBody
    public String curatorTest(Integer type, String path, String value){
        String rootPath = demoConfig.getString(DemoConfig.ZOOKEEPER_GROUP);
        path = rootPath + "/" + path;
        if(type == 1){
            // 新增节点
            try {
                curatorFramework.create().creatingParentContainersIfNeeded().forPath(path);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if(type == 2){
            // 新增节点及其数据
            try {
                curatorFramework.create().creatingParentContainersIfNeeded().forPath(path, value.getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(type == 3){
            // 更新节点及其数据
            try {
                curatorFramework.setData().forPath(path, value.getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if(type == 4) {
            // 删除节点
            try {
                curatorFramework.delete().deletingChildrenIfNeeded().forPath(path);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return "curator operator test";
    }

    @GetMapping("/mongo/save")
    @ResponseBody
    public Object mongoSave(String userName){
        UserLoginRecord userLoginRecord = new UserLoginRecord();
        userLoginRecord.setUserName(userName);
        userLoginRecord.setLoginCount(1);
        userLoginRecord.setLoginTime(new Date());
        userLoginRecord.setModifyTime(new Date());

        userLoginDAO.saveMongoEntity(userLoginRecord);

        return userLoginRecord;
    }

    @GetMapping("/mongo/update")
    @ResponseBody
    public Object mongoUpdate(String userName){
        Query queryList = new Query(where("userName").is(userName));
        List<UserLoginRecord> userLoginRecordList = userLoginDAO.query(queryList, UserLoginRecord.class);
        SafeNullUtils.safeGetStream(userLoginRecordList).forEach(userLoginRecord -> {

            Query updateQuery = new Query(where("_id").is(userLoginRecord.getId()));
            Update update = new Update();
            update.inc("loginCount", 1);
            update.currentDate("modifyTime");

            userLoginDAO.updateFirst(updateQuery, update, UserLoginRecord.class);

        });

        return userLoginRecordList;
    }

    @GetMapping("/mongo/delete")
    @ResponseBody
    public Object mongoDelete(String userName){
        Query queryList = new Query(where("loginCount").gte(1));
        // 模糊查询
        Pattern pattern = Pattern.compile("^." + userName + ".*$", Pattern.CASE_INSENSITIVE);
        queryList.addCriteria(Criteria.where("userName").regex(pattern));

        List<UserLoginRecord> userLoginRecordList = userLoginDAO.query(queryList, UserLoginRecord.class);
        SafeNullUtils.safeGetStream(userLoginRecordList).forEach(userLoginRecord -> {

            Query updateQuery = new Query(where("_id").is(userLoginRecord.getId()));

            userLoginDAO.remove(updateQuery, UserLoginRecord.class);
        });

        return userLoginRecordList;
    }

    @GetMapping("/mongo/query")
    @ResponseBody
    public Object mongoQuery(String userName){
        Query query = new Query(where("loginCount").gte(1));
        // 模糊查询
        Pattern pattern = Pattern.compile("^.*" + userName + ".*$", Pattern.CASE_INSENSITIVE);
        query.addCriteria(Criteria.where("userName").regex(pattern));

        log.info("one data: {}", userLoginDAO.queryOne(query, UserLoginRecord.class));

        List<UserLoginRecord> loginRecordList = userLoginDAO.query(query, UserLoginRecord.class);

        return loginRecordList;
    }

    @PostMapping("/test/query")
    @ResponseBody
    public Object testQuery(@RequestBody User user){
        System.out.println(user);
        return user;
    }
}
